﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;

using Manatee.Trello;

namespace AIC_Notification
{
    class TrelloProcessorGuidance : TrelloProcessorAIC
    {
        private string MonthlyPlanCardPattern = @"(?i:P)\s*[：:-]\s*(?<Year>\d{4})\s*[/.-]\s*(?<Month>\d{1,2})";
        private string MeetingRecapCardPattern = @"(?i:R)\s*[：:-]\s*(?<Year>\d{4})\s*[/.-]\s*(?<Month>\d{1,2})\s*[/.-]\s*(?<Day>\d{1,2})";
        private string ReportCardCardPattern = @"(?i:RC)\s*[：:-]\s*(?<Year>\d{4})\s*[/.-]\s*(?<Term>\w+)";

        public TrelloProcessorGuidance()
        {
            Program = "Guidance";
            CacheFileName = String.Format("AIC_Notification-{0}-Cache.json", Program);
            TempCacheFileName = String.Format("AIC_Notification-{0}-Cache-Temp.json", Program);
            BoardNamePattern = @"^(?<ApplicationYear>\d{4})\s+(?<Program>\w+)\s+(?<City>\w{2})\s+(?<StudentName>\w[\w\s]+\w)\s*[,，]\s*(?<AcademicConsultant1>\w[\w\s]+\w)\s*([&-]\s*(?<AcademicConsultant2>\w[\w\s]*\w)\s*)?[,，]\s*(?<EducationConsultant>\w[\w\s]+\w)";
        }

        public override void SetUpdate(ref Student ApplyingStudent, bool HasUpdate)
        {
            ApplyingStudent.HasGuidanceUpdate = HasUpdate;
        }

        public override bool GetUpdate(Student ApplyingStudent)
        {
            return ApplyingStudent.HasGuidanceUpdate;
        }

        public override void ReadBoard(Student ApplyingStudent)
        {
            Manatee.Trello.Board board = new Manatee.Trello.Board(ApplyingStudent.TrelloId); ;
            SetProcessingContext(board);

            Guidance_Application application = null;

            if (Global.Guidance_Applications.TryGetValue(ApplyingStudent.TrelloId, out application))
            {
                if (application != null)
                {
                    application.Reset();
                    Global.SysUtil.Log(String.Format("Upddating Existing Board: {0}", ApplyingStudent.Name), 1);
                }
            }

            if (application == null)
            {
                Global.SysUtil.Log(String.Format("Creating New Board: {0}", ApplyingStudent.Name), 1);
                application = new Guidance_Application(board.Name, board.Id, board.Url,
                                                       ApplyingStudent.ApplicationYear,
                                                       ApplyingStudent.Program,
                                                       ApplyingStudent.GetCity(),
                                                       ApplyingStudent.Name,
                                                       ApplyingStudent.PT,
                                                       ApplyingStudent.ST,
                                                       ApplyingStudent.EC);
                Global.Guidance_Applications.Add(board.Id, application);
            }

            ReadLists(board, ref application);
        }

        public override void AddStudentToLists()
        {
            List<string> StudentsToRemove = new List<string>();

            foreach (var student in Global.Guidance_Applications)
            {
                AIC_Application Application = student.Value;

                // Validate if application is still valid
                if (MatchBoardWithStudent(ref Application, Global.Guidance_StudentDB.Students))
                {
                    // Build Teacher List (PT, ST, EC)
                    AddStudentToTeacher(student.Value.AC_PT, student.Value);
                    if (student.Value.AC_ST != "")
                        AddStudentToTeacher(student.Value.AC_ST, student.Value);
                    AddStudentToTeacher(student.Value.EC, student.Value);

                    // Build City List
                    AddStudentToCity(student.Value);
                }
                else
                {
                    StudentsToRemove.Add(student.Key);
                    Global.SysUtil.Log(String.Format("Removing Board: {0}", student.Value.BoardName), 2);
                }
            }

            // Remove outdated records
            foreach (var StudentKey in StudentsToRemove)
                Global.Guidance_Applications.Remove(StudentKey);
        }

        public void AddStudentToCity(Guidance_Application student)
        {
            City TargetCity = CityDB.GetCityByName(student.City);

            if (TargetCity != null)
                TargetCity.GuidanceStudents.Add(student);
            else
                Global.SysUtil.Log(String.Format("Unable to insert \"{0}\" to City list: {1}", student.Name, student.City), 2);
        }

        public override void AddStudentToTeacher(string TeacherName, AIC_Application student)
        {
            Teacher teacher;
            AIC_Application hasStudent;

            teacher = TeacherDB.GetTeacherByName(TeacherName);
            if (teacher != null)
            {
                // Check if duplicate, add only if student not already in board
                hasStudent = teacher.Students.SingleOrDefault(AIC_Application => AIC_Application.Name.Equals(student.Name));
                if (hasStudent == null)
                {
                    teacher.GuidanceStudents.Add((Guidance_Application)student);
                    Global.SysUtil.Log(String.Format("Added Student: {0} to Teacher: {1}", student.Name, teacher.Name));
                }
            }
            else
            {
                Global.SysUtil.Log(String.Format("Teacher not found: {0} for student {1}", TeacherName, student.Name), 2);
            }
        }

        public void ReadLists(Manatee.Trello.Board board, ref Guidance_Application student)
        {
            bool HasMonthlyPlanList = false;
            bool HasProgressReportList = false;
            bool HasEvaluationReportCardList = false;

            // Fill in code to detect Monthly Plan and Progress Report

            foreach (var list in board.Lists)
            {
                SetProcessingContext(list);

                string ListName = list.Name;

                if (ListName.StartsWith("MP:"))
                {
                    if (!HasMonthlyPlanList)
                    {
                        student.MonthlyPlanList = new Guidance_MonthlyPlanList(list.Id);
                        student.MonthlyPlanList.SetList(ListName);
                        HasMonthlyPlanList = true;
                        Global.SysUtil.Log(String.Format("Monthly Plan Found"));

                        Global.SysUtil.Log("Process List: " + ProcessingList.Name);
                        ReadMonthlyPlanCards(list, ref student.MonthlyPlanList);
                    }
                    else
                        Global.InputErrors.Add(new Error(student, null, null, Error.Owner.admin, String.Format("Duplicate Monthly Plan List")));
                }
                else if (ListName.StartsWith("MR:"))
                {
                    if (!HasProgressReportList)
                    {
                        student.MeetingRecapList = new Guidance_MeetingRecapList(list.Id);
                        student.MeetingRecapList.SetList(ListName);
                        HasProgressReportList = true;
                        Global.SysUtil.Log(String.Format("Progress Report Found"));

                        Global.SysUtil.Log("Process List: " + ProcessingList.Name);
                        ReadMeetingRecapCards(list, ref student.MeetingRecapList);
                    }
                    else
                        Global.InputErrors.Add(new Error(student, null, null, Error.Owner.admin, String.Format("Duplicate Meeting Recap List")));
                }
                else if (ListName.StartsWith("ERC:"))
                {
                    if (!HasEvaluationReportCardList)
                    {
                        student.EvaluationReportCardList = new Guidance_EvaluationReportCardList(list.Id);
                        student.EvaluationReportCardList.SetList(ListName);
                        HasEvaluationReportCardList = true;
                        Global.SysUtil.Log(String.Format("Evaluation Report Card Found"));

                        Global.SysUtil.Log("Process List: " + ProcessingList.Name);
                        ReadReportCards(list, student, ref student.EvaluationReportCardList);
                    }
                    else
                        Global.InputErrors.Add(new Error(student, null, null, Error.Owner.admin, String.Format("Duplicate Evaluation Report Card List")));
                }

            }

            if (!HasMonthlyPlanList)
                Global.InputErrors.Add(new Error(student, null, null, Error.Owner.admin, String.Format("Missing Monthly Plan List")));
            if (!HasProgressReportList)
                Global.InputErrors.Add(new Error(student, null, null, Error.Owner.admin, String.Format("Missing Meeting Recap List")));
        }

        public void ReadMonthlyPlanCards(Manatee.Trello.List list, ref Guidance_MonthlyPlanList monthlyPlanList)
        {
            // Setup parser
            Regex MonthPlanCardRegex = new Regex(MonthlyPlanCardPattern, RegexOptions.None, TimeSpan.FromMilliseconds(150));

            foreach (var card in list.Cards)
            {
                // Try to validate list name
                SetProcessingContext(card);

                // Detech Comment Card that begins wtih #
                if (card.Name.StartsWith("#"))
                    continue;

                Match m;

                if (monthlyPlanList != null)
                {
                    m = MonthPlanCardRegex.Match(card.Name);
                    if (m.Success)
                    {
                        Guidance_MonthlyPlan NewMonthlyPlan = new Guidance_MonthlyPlan();

                        // Set information and validate
                        NewMonthlyPlan.Year = Convert.ToInt32(m.Groups["Year"].Value);
                        NewMonthlyPlan.Month = Convert.ToInt32(m.Groups["Month"].Value);
                        NewMonthlyPlan.SubmitDate = card.CreationDate;
                        NewMonthlyPlan.Url = card.Url;

                        // Check if has Checklist
                        if (card.CheckLists.Count() > 0)
                        {
                            NewMonthlyPlan.PlanCheckList = new Checklist();
                            NewMonthlyPlan.PlanCheckList.ChecklistName = card.CheckLists[0].Name;
                            NewMonthlyPlan.PlanCheckList.ReadListItems(card.CheckLists[0]);
                        }

                        Global.SysUtil.Log(String.Format("Monthly Plan: {0}-{1} on {2:d}", NewMonthlyPlan.Year, NewMonthlyPlan.Month, NewMonthlyPlan.SubmitDate));

                        monthlyPlanList.MonthlyPlanList.Add(NewMonthlyPlan);
                    }
                }
            }
        }

        public void ReadMeetingRecapCards(Manatee.Trello.List list, ref Guidance_MeetingRecapList meetingRecapList)
        {
            // Setup parser
            Regex MeetingRecapCardRegex = new Regex(MeetingRecapCardPattern, RegexOptions.None, TimeSpan.FromMilliseconds(150));

            foreach (var card in list.Cards)
            {
                // Try to validate list name
                SetProcessingContext(card);

                // Detech Comment Card that begins wtih #
                if (card.Name.StartsWith("#"))
                    continue;

                Match m;

                if (meetingRecapList != null)
                {
                    m = MeetingRecapCardRegex.Match(card.Name);
                    if (m.Success)
                    {
                        Guidance_MeetingRecap MeetingRecap = new Guidance_MeetingRecap();

                        // Set information and validate
                        MeetingRecap.Year = Convert.ToInt32(m.Groups["Year"].Value);
                        MeetingRecap.Month = Convert.ToInt32(m.Groups["Month"].Value);
                        MeetingRecap.Day = Convert.ToInt32(m.Groups["Day"].Value);
                        MeetingRecap.SubmitDate = card.CreationDate;
                        MeetingRecap.Url = card.Url;

                        Global.SysUtil.Log(String.Format("Meeting Recap: {0}-{1}-{2} on {3:d}", MeetingRecap.Year, MeetingRecap.Month, MeetingRecap.Day, MeetingRecap.SubmitDate));

                        meetingRecapList.MeetingRecapList.Add(MeetingRecap);
                    }
                }
            }
        }

        public void ReadReportCards(Manatee.Trello.List list, Guidance_Application student, ref Guidance_EvaluationReportCardList ReportCardList)
        {
            // Setup parser
            Regex ReportCardCardRegex = new Regex(ReportCardCardPattern, RegexOptions.None, TimeSpan.FromMilliseconds(150));

            foreach (var card in list.Cards)
            {
                // Try to validate list name
                SetProcessingContext(card);

                // Detech Comment Card that begins wtih #
                if (card.Name.StartsWith("#"))
                    continue;

                Match m;

                if (ReportCardList != null)
                {
                    m = ReportCardCardRegex.Match(card.Name);
                    if (m.Success)
                    {
                        Guidance_ReportCard ReportCard = new Guidance_ReportCard();

                        // Set information and validate
                        ReportCard.Year = Convert.ToInt32(m.Groups["Year"].Value);
                        if (m.Groups["Term"].Value.Equals("Spring", StringComparison.CurrentCultureIgnoreCase))
                        {
                            ReportCard.Term = Guidance_ReportCard.GuidanceTerm.Spring;
                        }
                        else if (m.Groups["Term"].Value.Equals("Summer", StringComparison.CurrentCultureIgnoreCase))
                        {
                            ReportCard.Term = Guidance_ReportCard.GuidanceTerm.Summer;

                        }
                        else if (m.Groups["Term"].Value.Equals("Fall", StringComparison.CurrentCultureIgnoreCase))
                        {
                            ReportCard.Term = Guidance_ReportCard.GuidanceTerm.Fall;
                        }
                        else
                        {
                            Global.InputErrors.Add(new Error(student, ReportCardList, ReportCard, Error.Owner.ac, String.Format("Unrecognize Guidance Term: {0}", m.Groups["Term"].Value)));
                        }
                        ReportCard.SubmitDate = card.CreationDate;
                        ReportCard.Url = card.Url;

                        if (card.Attachments.Count() > 0)
                        {
                            ReportCard.HasAttachment = true;
                            ReportCard.AttachmentName = card.Attachments[0].Name;
                        }

                        Global.SysUtil.Log(String.Format("Report Card: {0}-{1} on {2:d}", ReportCard.Year, ReportCard.Term, ReportCard.SubmitDate));

                        ReportCardList.ReportCards.Add(ReportCard);
                    }
                }
            }
        }
    }
}